 /*******************************************************************************
  * Copyright (c) 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  ******************************************************************************/

 package org.eclipse.ui.internal.menus;

 import java.util.ArrayList ;
 import java.util.HashMap ;
 import java.util.Iterator ;
 import java.util.List ;
 import java.util.Map ;

 import org.eclipse.core.expressions.Expression;
 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.core.runtime.IExtensionRegistry;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.jface.action.IContributionItem;
 import org.eclipse.jface.menus.IWidget;
 import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
 import org.eclipse.ui.internal.util.Util;
 import org.eclipse.ui.menus.IMenuService;
 import org.eclipse.ui.menus.IWorkbenchWidget;

 /**
  * Handles the top level caching for 3.2 style trim
  * contributions.
  *
  * @since 3.3
  *
  */
 public class TrimAdditionCacheEntry {
     private IConfigurationElement additionElement;
     private MenuLocationURI uri = null;
     
     /**
      * The map contains {@link IWorkbenchWidget} entries
      * for widgets that have failed to load on a previous
      * attempt. Used to prevent multiple retries at
      * loading a widget (which spams the log).
      */
     private Map failedWidgets = new HashMap ();
     /**
      * Maps the widget back to it's configurtation element
      */
     private Map widgetToConfigElementMap = new HashMap ();
     

     // Caches

     /**
      * Maps an IContributionItem to its corresponding IConfigurationElement
      */
     Map iciToConfigElementMap = new HashMap ();

     public TrimAdditionCacheEntry(IConfigurationElement element,
             MenuLocationURI uri, IMenuService service) {
         this.additionElement = element;
         this.uri = uri;
     }

     /**
      * Populate the list
      *
      * @param additions
      */
     public void getContributionItems(List additions) {
         additions.clear();

     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.internal.menus.MenuCacheEntry#generateSubCaches()
      */
     public void generateSubCaches() {
         // TODO Auto-generated method stub

     }

     /* (non-Javadoc)
      * @see org.eclipse.ui.internal.menus.MenuCacheEntry#getVisibleWhenForItem(org.eclipse.jface.action.IContributionItem)
      */
     public Expression getVisibleWhenForItem(IContributionItem item) {
         // TODO Auto-generated method stub
 return null;
     }

     /**
      * @return
      */
     public String getId() {
         return additionElement.getAttribute(IWorkbenchRegistryConstants.ATT_ID);
     }

     /**
      * @return <code>true</code> iff the group is positioned at the start
      * (or 'before') the entry that it is relative to. Default is true
      *
      */
     public boolean isAtStart() {
         IConfigurationElement location = additionElement.getChildren(IWorkbenchRegistryConstants.TAG_LOCATION)[0];
         if (location.getChildren(IWorkbenchRegistryConstants.TAG_ORDER).length > 0) {
             IConfigurationElement order = location.getChildren(IWorkbenchRegistryConstants.TAG_ORDER)[0];
             
             String pos = order.getAttribute(IWorkbenchRegistryConstants.ATT_POSITION);
             if (pos != null)
                 return (pos.equals("start") | pos.equals("before")); //$NON-NLS-1$//$NON-NLS-2$
 }
         return true;
     }

     /**
      * Returns whether or not the defining {@link IConfigurationElement}
      * declares that the widget should use extra space in the 'major'
      * dimension (ie. use extra horizontal space in the status area).
      * The space is equally divided with other elementa in the same
      * trim area that also want to use the extra space.
      *
      * @param widgetElement the {@link IConfigurationElement} declaring this
      * widget.
      *
      * @return <code>true</code> iff the resulting widget should use
      * extra major space
      */
     public boolean fillMajor(IConfigurationElement widgetElement) {
         if (widgetElement.getChildren(IWorkbenchRegistryConstants.TAG_LAYOUT).length==0) {
             return false;
         }
         IConfigurationElement layout = widgetElement.getChildren(IWorkbenchRegistryConstants.TAG_LAYOUT)[0];
         String fillMajorVal = layout.getAttribute(IWorkbenchRegistryConstants.ATT_FILL_MAJOR);

         return (fillMajorVal != null && fillMajorVal.equals("true")); //$NON-NLS-1$
 }

     /**
      * Returns whether or not the defining {@link IConfigurationElement}
      * declares that the widget should use extra space in the 'minor'
      * dimension (ie. use extra vertical space in the status area)
      *
      * @param widgetElement the {@link IConfigurationElement} declaring this
      * widget.
      *
      * @return <code>true</code> iff the resulting widget should use
      * extra minor space
      */
     public boolean fillMinor(IConfigurationElement widgetElement) {
         if (widgetElement.getChildren(IWorkbenchRegistryConstants.TAG_LAYOUT).length==0) {
             return false;
         }
         IConfigurationElement layout = widgetElement.getChildren(IWorkbenchRegistryConstants.TAG_LAYOUT)[0];
         String fillMinorVal = layout.getAttribute(IWorkbenchRegistryConstants.ATT_FILL_MINOR);

         return (fillMinorVal != null && fillMinorVal.equals("true")); //$NON-NLS-1$
 }

     /**
      * @return The list of IConfigurationElements representing
      * widgets to be added into this 'group'
      */
     private List getWidgetConfigs() {
         List widgetConfigs = new ArrayList ();
         
         // Return to the 'root' of the config tree and gather all elements
 // for this 'group'. Note that while this is sub-optimal
 // performace-wise that there are expected to be -very-
 // few contributions in total (i.e. 10's, not 100's)
 final IExtensionRegistry registry = Platform.getExtensionRegistry();
         final IConfigurationElement[] widgetElements = registry
                 .getConfigurationElementsFor(IWorkbenchRegistryConstants.EXTENSION_MENUS);

         // Locate all 'widget' additions appropriate for -this- group
 for (int i = 0; i < widgetElements.length; i++) {
             // Only process 'widget' entries
 if (!IWorkbenchRegistryConstants.TAG_WIDGET.equals(widgetElements[i].getName()))
                 continue;
             
             // Define the initial URI spec
 if (widgetElements[i].getChildren(IWorkbenchRegistryConstants.TAG_LOCATION).length > 0) {
                 IConfigurationElement location = widgetElements[i].getChildren(IWorkbenchRegistryConstants.TAG_LOCATION)[0];
                 if (location.getChildren(IWorkbenchRegistryConstants.TAG_BAR).length > 0) {
                     IConfigurationElement bar = location.getChildren(IWorkbenchRegistryConstants.TAG_BAR)[0];

                     // The bar's path represents the 'group' it should go into
 String path = bar.getAttribute(IWorkbenchRegistryConstants.ATT_PATH);
                     if (path != null && path.equals(getId()))
                             widgetConfigs.add(widgetElements[i]);
                 }
             }
         }
         
         return widgetConfigs;
     }
     
     /**
      * Attempts to load -all- widgets for this entry and
      * keeps track of the successful loads only. Only elements
      * who can be successfully loaded will be seen by the
      * builder.
      *
      * @return The list of <code>IWorkbenchWidget</code> entries
      * that have been successfully loaded
      */
     public List getWidgets() {
         List loadedWidgets = new ArrayList ();
         
         // Get the widget config elements for this 'group'
 List widgetConfigs = getWidgetConfigs();
         for (Iterator iterator = widgetConfigs.iterator(); iterator
                 .hasNext();) {
             IConfigurationElement widgetCE = (IConfigurationElement) iterator.next();
             
             // skip elements that are known to fail
 if (failedWidgets.containsKey(widgetCE))
                 continue;
             
             IWorkbenchWidget loadedWidget = loadWidget(widgetCE);

             // Either add it to the 'valid' list or mark it
 // as failed
 if (loadedWidget != null) {
                 loadedWidgets.add(loadedWidget);
                 widgetToConfigElementMap.put(loadedWidget, widgetCE);
             }
             else
                 failedWidgets.put(widgetCE, widgetCE);
         }
         
         return loadedWidgets;
     }

     /**
      * Attempts to load the executable extension defined within the given
      * configuration element. An error is logged for any widget that fails
      * to load.
      *
      * @param widgetCE The {@link IConfigurationElement} containing the
      * widget's 'class' specification.
      *
      * @return The loaded {@link IWorkbenchWidget} or <code>null</code>
      * if the loading fails
      */
     private IWorkbenchWidget loadWidget(IConfigurationElement widgetCE) {
         return (IWorkbenchWidget) Util.safeLoadExecutableExtension(widgetCE,
                     IWorkbenchRegistryConstants.ATT_CLASS,
                     IWorkbenchWidget.class);
     }

     /**
      * @param widget The {@link IWorkbenchWidget} to get the defining configuration
      * element for.
      *
      * @return The defining {@link IConfigurationElement}
      */
     public IConfigurationElement getElement(IWorkbenchWidget widget) {
         return (IConfigurationElement) widgetToConfigElementMap.get(widget);
     }

     /**
      * @param widget
      */
     public void removeWidget(IWidget widget) {
         widgetToConfigElementMap.remove(widget);
     }
     
     public MenuLocationURI getUri() {
         return uri;
     }
 }

